package app.dao;

import app.vo.SearchVo;
import app.vo.User;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.bson.conversions.Bson;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;

import static com.mongodb.client.model.Filters.and;

public class MongoDBConnection {
   /** TODO update the DATABASE_URL value below 
    *  format: mongodb+srv://username:password@atlas.server.address/database
    *  Goto https://cloud.mongodb.com/ 
    *  click connect > connect your application > Driver Java > Version 4.3 or later
    *  copy connection string
    *  if you have time out issues (due to firewalls) choose verion 3.3 or earlier instead
    **/
   private static final String DATABASE_URL = "mongodb+srv://s3614926:Zjh8801222@dba-cluster.ny2rz.mongodb.net/test";
   static MongoClient client;
   MongoDatabase database;
   MongoCollection<Document> allListings;
   MongoCollection<Document> allComment;
   private static MongoDBConnection mongodb = null;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

   public static MongoDBConnection getConnection(){
      //check that MongoDBConnection is available (if not establish)
      if(mongodb==null){
         mongodb = new MongoDBConnection();
      }
      return mongodb;
   }
   public MongoDBConnection() {
      System.out.println("Creating MongoDB Connection Object");
      
      try {
         client = MongoClients.create(DATABASE_URL);
         database = client.getDatabase("sample_airbnb");
         allListings = database.getCollection("listingsAndReviews");
          allComment = database.getCollection("comment_list");
         } catch (Exception e) {
         // If there is an error, lets just print the error
         System.err.println(e.getMessage());
      }
   }
   
   public static void closeConnection(){
      try {
         if (client != null) {
            client.close();
            System.out.println("Database Connection closed");
         }
      } catch (Exception e) {
         // connection close failed.
         System.err.println(e.getMessage());
      }
   }

   public ArrayList<Object> getHost(String id){
      MongoCursor<Document> cursor = allListings.find(Filters.eq("_id", id)).iterator();
       ArrayList<Object> apartments = new ArrayList<Object>();
       try {
          while (cursor.hasNext()) {
            apartments.add(cursor.next());
          }
       }
       finally {
          cursor.close();
       }
       return apartments;
    }

   public ArrayList<Object> page(SearchVo vo, int pageNum){
       MongoCursor<Document> cursor = allListings.find(and(getBson(vo)))
               .sort(Sorts.ascending( "_id"))
               .skip((pageNum - 1) * 10).limit(10)
               .iterator();
       ArrayList<Object> apartments = new ArrayList<>();
       try {
           while (cursor.hasNext()) {
               apartments.add(cursor.next());
           }
       }
       finally {
           cursor.close();
       }
       return apartments;
   }
    public long pageCount(SearchVo vo) {
       return allListings.countDocuments(and(getBson(vo)));
    }
   public List<Bson> getBson(SearchVo vo) {
       List<Bson> bsons = new ArrayList<>();
       if (StringUtils.isNoneEmpty(vo.getLocation())) {
           String[] location =  vo.getLocation().split("-");
           bsons.add(Filters.eq("address.country", location[0]));
           if (location.length == 2){
               bsons.add(Filters.eq("address.market", location[1]));
           }
       }
       if (vo.getBedrooms() > 0) {
           bsons.add(Filters.gte("bedrooms", vo.getBedrooms()));
       }
       if (vo.getBeds() > 0) {
           bsons.add(Filters.gte("beds", vo.getBeds()));
       }
       if (StringUtils.isNoneEmpty(vo.getProperty())){
           bsons.add(Filters.eq("property_type", vo.getProperty()));
       }
       if (vo.getAccommodated() > 0) {
           bsons.add(Filters.gte("accommodates", vo.getAccommodated()));
       }
       if (StringUtils.isNoneEmpty(vo.getAmenities())){
//           bsons.add(Filters.elemMatch("amenities", Filters.eq(vo.getAmenities())));
           bsons.add(Filters.in("amenities", vo.getAmenities()));
       }
       if (vo.getPrice() > 0) {
           bsons.add(Filters.gte("price", vo.getPrice()));
       }
       if (vo.getRating() > 0) {
           bsons.add(Filters.gte("review_scores.review_scores_rating", vo.getRating()));
       }
       if (StringUtils.isNoneEmpty(vo.getStatus())){
           bsons.add(Filters.eq("host.host_is_superhost", Boolean.parseBoolean(vo.getStatus())));
       }
       if (StringUtils.isNoneEmpty(vo.getSummary())){
           bsons.add(Filters.regex("summary", vo.getSummary()));
       }
       if (bsons.size() == 0) {
           bsons.add(Filters.ne("_id", ""));
       }
       return bsons;
   }

   /**
    * _id
    * host_id
    * user_id
    * user_name
    * time
    * content
    */
   public long hostComCount(String id) {
       return allComment.countDocuments(Filters.eq("host_id", id));
   }
    public ArrayList<Object> getHostCom(String id, int pageNum) {
        MongoCursor<Document> cursor = allComment.find(Filters.eq("host_id", id))
                .sort(Sorts.descending( "time"))
                .skip((pageNum - 1) * 10).limit(10)
                .iterator();
        ArrayList<Object> com = new ArrayList<Object>();
        try {
            while (cursor.hasNext()) {
                com.add(cursor.next());
            }
        }
        finally {
            cursor.close();
        }
        return com;
    }

    public void insertCom(User user, String hostId, String content, String name) {
        Document document = new Document();
        document.append("_id", UUID.randomUUID().toString().replaceAll("-", ""));
        document.append("host_id", hostId);
        document.append("host_name", name);
        document.append("user_id", user.getId());
        document.append("user_name", user.getUsername());
        document.append("time", sdf.format(new Date()));
        document.append("content", content);
        allComment.insertOne(document);
    }

    public boolean updateCom(String id, String content){
       try{
           List<Document> d = new ArrayList<>();
           Document d1 = new Document("$set", new Document("content", content));
           Document d2 = new Document("$set", new Document("time", sdf.format(new Date())));
           d.add(d1);
           d.add(d2);
           allComment.updateOne(Filters.eq("_id", id), d);
           return true;
       } catch (Exception e) {
           e.printStackTrace();
           return false;
       }
    }
    public boolean deleteCom(String id) {
        try{
            allComment.deleteOne(Filters.eq("_id", id));
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    public ArrayList<Object> getHostComByUserId(String id, int pageNum) {
        MongoCursor<Document> cursor = allComment.find(Filters.eq("user_id", id))
                .sort(Sorts.descending( "time"))
                .skip((pageNum - 1) * 10).limit(10)
                .iterator();
        ArrayList<Object> com = new ArrayList<Object>();
        try {
            while (cursor.hasNext()) {
                com.add(cursor.next());
            }
        }
        finally {
            cursor.close();
        }
        return com;
    }
    public long hostComCountByUserId(String id) {
        return allComment.countDocuments(Filters.eq("user_id", id));
    }
}
